package org.codegen.service.impl;

import org.codegen.service.CodeGenerator;
import org.codegen.service.CodeGeneratorManager;
import org.codegen.util.*;
import freemarker.template.Configuration;
import org.codegen.util.*;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;

/**
 * Controller层 代码生成器 Created by zhh on 2017/09/20.
 */
public class ControllerGenerator extends CodeGeneratorManager implements CodeGenerator {

    public void genCode(String tableName) {
        String modelName = StringUtils.tableNameConvertUpperCamel(tableName);
        Configuration cfg = getFreemarkerConfiguration();
        String customMapping = "/";
        String modelNameUpperCamel = modelName;
        String modelNameLowerCase = modelName.toLowerCase();
        //保存路径改变
        String localControllerPath = PACKAGE_PATH_CONTROLLER.replaceAll(Const.TABLE_LOWER_CASE, modelNameLowerCase);

        Map<String, Object> data = DataUtil.getDataMapInit(tableName, modelName, modelNameUpperCamel, modelNameLowerCase);
        try {
            List<String> controllerMethodsList = getControllerMethods(
                    getMapperJavaFilePath(tableName, modelName, customMapping, modelNameUpperCamel),
                    modelNameUpperCamel);
            data.put("controllerMethodsList", controllerMethodsList);
            File controllerFile = new File(TARGET_PROJECT_PATH + JAVA_PATH + localControllerPath + customMapping
                    + modelNameUpperCamel + "Controller.java");
            if (!controllerFile.getParentFile().exists()) {
                controllerFile.getParentFile().mkdirs();
            }
            cfg.getTemplate("controller.ftl").process(data, new FileWriter(controllerFile));
            logger.info(modelNameUpperCamel + "Controller.java 生成成功!");
        } catch (Exception e) {
            throw new RuntimeException("Controller 生成失败!", e);
        }
    }

    private String getMapperJavaFilePath(String tableName, String modelName, String customMapping,
                                         String modelNameUpperCamel) {
        String path = TARGET_PROJECT_PATH + JAVA_PATH + genLocalPath(PACKAGE_PATH_MAPPER, modelName) + customMapping + modelNameUpperCamel
                + "DAO.java";
        return path;
    }

    /**
     * 从给定的Mapper及其中的方法，生成Controller需要的方法
     *
     * @param mapperJavaFilePath
     * @return
     * @throws IOException
     */
    public static List<String> getControllerMethods(String mapperJavaFilePath, String modelName) throws IOException {
        Map<String, String> methodActionTypeMap = new HashMap<String, String>();
        Map<String, String> methodContentMap = new HashMap<String, String>();
        String methodPattern = "([^import|^package]).*;";
        List<String> contentList = FileUtil.readFile2List(mapperJavaFilePath);
        for (String content : contentList) {
            boolean match = Pattern.matches(methodPattern, content);
            if (match) {
                // 去掉前后的空格
                content = content.trim();
                String methodReturnType = MethodUtil.getMethodReturnType(content);
                String methodName = MethodUtil.getMethodName(content);
                List<String> methodParamTypeList = MethodUtil.getMethodParamTypeList(content);
                List<String> methodParanNameList = MethodUtil.getMethodParamNameList(content);
                String methodActionType = MethodUtil.getMethodActionType(content);
                String serviceName = StringUtils.toLowerCaseFirstOne(modelName) + "Sv";
                // 对相同功能的方法进行去除，如insert和insertSelective方法功能上相同，但考虑到insertSelective
                // 具有更多的灵活性，因而在生成Controller的时候，就考虑保留了它
                String oldMethodName = methodActionTypeMap.get(methodActionType);
                if (oldMethodName != null) {
                    if (methodName.indexOf("Selective") > 0) {
                        String methodNameWithoutSelective = methodName.replace("Selective", "");
                        if (oldMethodName.equals(methodNameWithoutSelective)) {
                            methodActionTypeMap.remove(methodActionType);
                            methodContentMap.remove(oldMethodName);
                        }
                    } else {
                        String methodNameWithSelective = methodName + "Selective";
                        if (oldMethodName.equals(methodNameWithSelective)) {
                            continue;
                        }
                        methodActionType = methodName;
                    }
                    /*
                     * if (methodActionTypeMap.get(methodActionType).length() > methodName.length()) { continue; } else
                     * if (methodActionTypeMap.get(methodActionType).length() < methodName.length()) {
                     * methodActionTypeMap.remove(methodActionType); }
                     */
                }
                methodActionTypeMap.put(methodActionType, methodName);
                StringBuilder methodContent = new StringBuilder();
                if (!"selectByPage".equals(methodName)) {
                    methodContent.append(genComment(methodName, methodParamTypeList, methodParanNameList, methodReturnType));
                    methodContent.append(StringUtils.FOUR_SPACES).append("@RequestMapping(\"/" + methodActionType + "\")\n");
                    methodContent.append(StringUtils.FOUR_SPACES).append("@ResponseBody\n");
                    methodContent.append(StringUtils.FOUR_SPACES).append("public EastMoneyResponse ").append(methodName).append("(");
                    int index = 0;
                    for (String methodParamType : methodParamTypeList) {
                        methodContent.append(MethodUtil.getMethodParamType(methodName, methodParamType, modelName));
                        methodContent.append(" ").append(methodParanNameList.get(index));
                        if (index < methodParamTypeList.size() - 1) {
                            methodContent.append(", ");
                        }
                        index++;
                    }

                    methodContent.append(")").append("{\n");
                    methodContent.append(genMethord(methodReturnType, methodParanNameList, serviceName, methodName));
                    methodContent.append(StringUtils.FOUR_SPACES).append("}\n");
                }
                methodContentMap.put(methodActionType, methodContent.toString());
                if ("selectByCondition".equals(methodName)) {
                    StringBuilder methodContentInPage = new StringBuilder();
                    methodContent.append(genComment("selectByConditionInPage", methodParamTypeList, methodParanNameList, "EastMoneyResponse"));
                    methodContent.append(StringUtils.FOUR_SPACES).append("@RequestMapping(\"/" + methodActionType + "/inPage\")\n");
                    methodContent.append(StringUtils.FOUR_SPACES).append("@ResponseBody\n");
                    methodContent.append(StringUtils.FOUR_SPACES).append("public EastMoneyResponse ").append(methodName + "InPage").append("(");
                    int index = 0;
                    for (String methodParamType : methodParamTypeList) {
                        methodContent.append(MethodUtil.getMethodParamType(methodName, methodParamType, modelName));
                        methodContent.append(" ").append(methodParanNameList.get(index));
                        if (index < methodParamTypeList.size() - 1) {
                            methodContent.append(", ");
                        }
                        index++;
                    }

                    methodContent.append(")").append("{\n");
                    methodContent.append(StringUtils.EIGHT_SPACES).append("PageHelper.startPage(PaginationContext.getPageNum(), PaginationContext.getPageSize());\n" +
                            "        " + methodReturnType + " resp = "+serviceName+".selectByCondition(record);\n" +
                            "        PageBean<"+methodParamTypeList.get(0)+"> pageInfo = new PageBean <>(resp);\n" +
                            "        return encapsule(pageInfo, Constants.ResponseState.SUCCESS);\n");
                    methodContent.append(StringUtils.FOUR_SPACES).append("}\n");
                    methodContentMap.put(methodActionType, methodContent.toString());
                }
            }
        }
        List<String> serviceMethodsContentList = new ArrayList<String>();
        serviceMethodsContentList.addAll(methodContentMap.values());
        return serviceMethodsContentList;
    }


    @Deprecated
    public static String genTryCatchCode(String methodReturnType, List<String> methodParanNameList, String serviceName, String methodName) {
        StringBuilder methodContent = new StringBuilder();
        StringBuilder params = new StringBuilder();
        for (String s : methodParanNameList) {
            params.append(",").append(s);
        }
        methodContent.append(StringUtils.EIGHT_SPACES)
                .append("Object[] objects = {").append(
                params.length() > 0 ? params.substring(1) : ""
        ).append("};").append("\n");
        methodContent.append(StringUtils.EIGHT_SPACES)
                .append("return baseTryCatch(").append(serviceName).append(",\"").append(methodName).append("\",objects);").append("\n");
        return methodContent.toString();
    }

    @Deprecated
    public static String genTryCatchCodeOld(String methodReturnType, List<String> methodParanNameList, String serviceName, String methodName) {
        StringBuilder methodContent = new StringBuilder();
        methodContent.append(StringUtils.EIGHT_SPACES).append(methodReturnType).append(" resp;").append("\n");
        methodContent.append(StringUtils.EIGHT_SPACES).append("try {").append("\n");
        methodContent.append(StringUtils.TWELVE_SPACES)
                .append("resp = ").append(serviceName).append("." + methodName).append("(")
                .append(MethodUtil.listToString(methodParanNameList)).append(");").append("\n");
        methodContent.append(StringUtils.EIGHT_SPACES).append("} catch (BusinessException e) {").append("\n");
        methodContent.append(StringUtils.TWELVE_SPACES).append("LOG.error(e.getExcMsg());").append("\n");
        methodContent.append(StringUtils.TWELVE_SPACES).append("return encapsule(new EmptyResponse(), -1,e.getExcMsg());").append("\n");
        methodContent.append(StringUtils.EIGHT_SPACES).append("} catch (Exception e) {").append("\n");
        methodContent.append(StringUtils.TWELVE_SPACES).append("LOG.error(e.getMessage());").append("\n");
        methodContent.append(StringUtils.TWELVE_SPACES).append("return encapsule(new EmptyResponse(), -1, \"后台sql执行报错!\");").append("\n");
        methodContent.append(StringUtils.EIGHT_SPACES).append("}").append("\n");
        methodContent.append(StringUtils.EIGHT_SPACES).append("return encapsule(");
        if (methodReturnType.equals("int") || methodReturnType.equals("Integer")) {
            methodContent.append("new ObjectResponse(").append("resp").append(")");
        } else {
            methodContent.append("resp");
        }
        methodContent.append(", Constants.ResponseState.SUCCESS);").append("\n");
        return methodContent.toString();
    }

    public static String genMethord(String methodReturnType, List<String> methodParanNameList, String serviceName, String methodName) {
        StringBuilder methodContent = new StringBuilder();
        methodContent.append(StringUtils.EIGHT_SPACES).append(methodReturnType).append(" resp = ")
                .append(serviceName).append("." + methodName).append("(")
                .append(MethodUtil.listToString(methodParanNameList)).append(");").append("\n");
        methodContent.append(StringUtils.EIGHT_SPACES).append("return encapsule(");
        if (methodReturnType.equals("int") || methodReturnType.equals("Integer")) {
            methodContent.append("new ObjectResponse(").append("resp").append(")");
        } else {
            methodContent.append("resp");
        }
        methodContent.append(", Constants.ResponseState.SUCCESS);").append("\n");
        return methodContent.toString();
    }
}
